#!/usr/bin/env python

# pita: An invisible audio player
# Copyright (C) 2006-2007 Austin Bingham
#
# This program is free software# you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation# either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY# without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program# if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# You can reach the author at:
#     austin.bingham@gmail.com

import cStringIO, dircache
import logging, os, os.path, urllib
from pita.client_util import *
import pita.client_util as cutil
import pita.config as cfg
import pita, pita.app

import cherrypy

ProxyError = 'Unable to establish server proxy'

logger = logging.getLogger('pitaweb')

def secure_path(root_dir, path):
    '''
    This determines if root_dir + path is actually under
    root_dir. If so, this returns True, otherwise False
    '''
    full_path = os.path.abspath(os.path.join(root_dir, path))
    pref = os.path.commonprefix([root_dir, full_path])
    if pref != root_dir:
        return False
    return True

def refresh(f):
    '''
    This decorates a function such that it returns
    HTML which causes an immediate refresh/redirect
    to index()
    '''
    def wrapper(self, *args, **kwargs):
        f(self, *args, **kwargs)
        return '<head><meta http-equiv="refresh" content="0;/"></head>'
    wrapper.__name__ = f.__name__
    wrapper.__dict__ = f.__dict__
    wrapper.__doc = f.__doc__
    return wrapper

class PitaWeb:
    def __init__(self, server, root_dir):
        self.server = server
        self.root_dir = root_dir
        self.path = []
        self.curr_tab = 'playlist'
        self.tab_defs = [
            ['playlist', 'Playlist', self.playlist, True],
            ['files',    'Files',    self.files,    False]
            ]
        

    @cherrypy.expose
    def css(self):
        out = cStringIO.StringIO()

        out.write('''
        body { 
	  font: 8pt/16pt helvetica; 
	  color: #000000; 
	  background: #fff; 
	  margin: 0px; 
	}
        p { 
	  font: 8pt/16pt georgia; 
	  margin-top: 0px; 
	  text-align: justify;
	}
        h3 { 
	  font: italic normal 12pt georgia; 
	  letter-spacing: 1px; 
	  margin-bottom: 0px; 
	  color: #7D775C;
	}
        a:link { 
	  /* font-weight: normal; */
	  text-decoration: none; 
	  color: #000000;
	}
        a:visited { 
	  /* font-weight: bold; */
	  text-decoration: none; 
	  color: #000000;
	}
        a:hover, a:active { 
	  /* text-decoration: underline; */
	  color: #ff0000;
	}

        table.toolbar {
          background: #ffffff;
          border-spacing: 2px;
        }

        table.tabbed {
          border-spacing: 0px;
        }

        td.tab1 {
          border-style: solid;
          border-width: 1px;
        }

        td.tab {
          border-style: solid;
          border-left-width: 0px;
          border-top-width: 1px;
          border-bottom-width: 1px;
          border-right-width: 1px;
        }

        td.selected_tab {
          background: yellow;
        }

        td.button {
          border-style: solid;
          border-width: 1px;
          background: #dddddd;
        }

        tr.playlist_selection {
          background: yellow;
        }

        /* specific divs */
        #intro { 
	  min-width: 470px;
	}
        ''')
        return out.getvalue()

    @cherrypy.expose
    @refresh
    def set_tab(self, tab):
        self.curr_tab = tab

    @cherrypy.expose
    @refresh
    def next(self):
        self.server.next()

    @cherrypy.expose
    @refresh
    def previous(self):
        self.server.previous()

    @cherrypy.expose
    @refresh
    def pause_play(self):
        self.server.pause_play()

    @cherrypy.expose
    @refresh
    def set_random(self, r):
        self.server.set_random(bool(int(r)))

    @cherrypy.expose
    @refresh
    def cd(self, path=''):
        '''
        This takes in a relative path, and updates self.path
        to that path. If root_dir + path is not under root_dir,
        the path argument is set to root_dir.
        '''
        if not secure_path(self.root_dir, path):
            path = ''

        self.path = []
        head, tail = os.path.split(path)
        while len(tail) > 0:
            self.path.append(tail)
            head, tail = os.path.split(head)
        self.path.reverse()

    @cherrypy.expose
    @refresh
    def jump_to(self, index):
        self.server.jump_to(int(index))

    @cherrypy.expose
    @refresh
    def append_playlist(self, path, clear='0'):
        clear = int(clear)
        
        if not secure_path(self.root_dir, path):
            return
        good,bad = fix_and_test_paths([os.path.join(self.root_dir, path)])
        if len(bad) > 0:
            pass
        else:
            if clear: self.server.clear()
            self.server.append(good)
            self.server.unpause()

    @cherrypy.expose
    @refresh
    def remove(self, index):
        self.server.remove(int(index))

    def playlist(self, out):
        out.write('<table>')
        playlist = net_to_playlist(self.server.get_playlist())
        for idx in range(len(playlist)):
            sel_class = ''
            if idx == playlist.current:
                sel_class = 'playlist_selection'

            out.write('<tr class="%s">' % sel_class)
            
            out.write('<td><a href="jump_to?%s">%s</a></td>' %
                      (urllib.urlencode({'index':idx}),
                       playlist[idx]))

            out.write('<td class="button"><a href="remove?%s">remove</a></td>' %
                      urllib.urlencode({'index':idx}))

            out.write('</tr>')
        out.write('</table>')

    def files(self, out):
        if not self.root_dir: return
        
        out.write('<h1><a href="cd">%s</a>' % self.root_dir)

        path = ''
        for idx in range(len(self.path)):
            path = os.path.join(path, self.path[idx])
            out.write(' / <a href="cd?%s">%s</a>' %
                      (urllib.urlencode({'path' : path}), self.path[idx]))
        out.write('</h1><p>')
        
        out.write('<table>')
        for d in dircache.listdir(os.path.join(self.root_dir, path)):
            out.write('<tr>')
                
            if os.path.isdir(os.path.join(self.root_dir, path, d)):
                out.write('<td><a href="cd?%s">%s</a></td>' %
                           (urllib.urlencode({'path' : os.path.join(path, d)}), d))
            else:
                out.write('<td>%s</td>' % d)

            out.write('<td class="button"><a href="append_playlist?%s">play</a></td>' %
                      urllib.urlencode({'path' : os.path.join(path, d),
                                        'clear' : '1'}))
            out.write('<td class="button" align="right"><a href="append_playlist?%s">enqueue</a></td>' %
                      urllib.urlencode({'path' : os.path.join(path, d)}))
            out.write('</tr>')
        out.write('</table>')

    def print_control_bar(self, out):
        out.write('''
        <table class="toolbar">
        <tr>
        <td class="button"><a href="previous">Previous</a></td>
        <td class="button"><a href="next">Next</a></td>
        ''')

        if self.server.is_paused():
            pp_text = 'Play'
        else:
            pp_text = 'Pause'
        out.write('<td class="button"><a href="pause_play">%s</a></td>' % pp_text)
        
        if self.server.is_random():
            out.write('<td class="button"><a href="set_random?%s">Unset Random</a></td>'
                      % urllib.urlencode({'r':'0'}))
        else:
            out.write('<td class="button"><a href="set_random?%s">Set Random</a></td>'
                      % urllib.urlencode({'r':'1'}))

        out.write('</tr></table>')

    @cherrypy.expose
    def index(self):
        def make_row(out, data):
            out.write('<tr><td>')
            data()
            out.write('</td></tr>')

        rval = cStringIO.StringIO()

        sel_tdef = None
        for idx in range(len(self.tab_defs)):
            tdef = self.tab_defs[idx]
            if self.curr_tab == tdef[0]:
                sel_tdef = tdef

        rval.write('''
        <!--
        <!DOCTYPE html
	PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        -->
        
        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
        <head>''')
        
        if sel_tdef and sel_tdef[3]:
            rval.write('<meta http-equiv="refresh" content="20;/">')
        
        rval.write('''
        <style type="text/css" media="all">
            @import "css";
          </style>
        </head>

        <body>
        ''')

        self.print_control_bar(rval)

        rval.write('<hr>')

        rval.write('''
        <table class="tabbed">
        <tr>
        ''')

        for idx in range(len(self.tab_defs)):
            tdef = self.tab_defs[idx]
            sel_class = ''
            if self.curr_tab == tdef[0]:
                sel_class = 'selected_tab'

            fclass = ''
            if idx == 0:
                fclass = 'tab1'

            rval.write('<td class="tab %s %s"><a href="set_tab?%s">%s</a></td>'
                       % (sel_class,
                          fclass,
                          urllib.urlencode({'tab':tdef[0]}),
                          tdef[1]))
        rval.write('</tr></table>')

        if sel_tdef:
            sel_tdef[2](rval)

        rval.write('</body></html>')
            
        return rval.getvalue()

class Application(pita.app.ClientApplication):
    def __init__(self):
        pita.app.ClientApplication.__init__(self, "pitaweb")

        pita.conf.add_option(
            cfg.Option('http_port',
                       '-P',
                       '--http-port',
                       ['pitaweb', 'port'],
                       8080,
                       int))
        
        pita.conf.add_option(
            cfg.Option('root_dir',
                       '-r',
                       '--root-dir',
                       ['pitaweb', 'root_dir']))

    def usage(self):
        return 'usage: %prog [options]'

    def run(self, args):
        if not pita.conf.root_dir:
            logger.warning('No root directory specified!')
            
        server = cutil.get_server_proxy_or_die(True)
        cherrypy.quickstart(PitaWeb(server, pita.conf.root_dir))

if __name__ == '__main__':
    app = Application()
    app.start()
